
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include "../imagepipe.h"

//#define GET_PIXEL(IMG, X, Y) (IMG->buf[ ((Y) * IMG->width + (X)) ])

#define GET_PIXEL(IMG, X, Y) (IMG.pixel( X, Y ))
//IMG->buf[ ((Y) * IMG->width + (X)) ])

//img.pixel( x, y );

#define RADIUS 1

typedef unsigned char color_component;
typedef color_component pixel[3];

typedef double luminance;

typedef struct {
    unsigned int width;
    unsigned int height;
    pixel * buf;
} image_t;
typedef image_t * image;
 

image alloc_img(unsigned int width, unsigned int height)
{
    image img;
    img = (image)malloc(sizeof(image_t));
    img->buf = (pixel*)malloc(width * height * sizeof(pixel));
    img->width = width;
    img->height = height;
    return img;
}
 
void free_img(image img)
{
    free(img->buf);
    free(img);
}
 
void fill_img(
        image img,
        color_component r,
        color_component g,
        color_component b )
{
    unsigned int i, n;
    n = img->width * img->height;
    for (i=0; i < n; ++i)
    {
        img->buf[i][0] = r;
        img->buf[i][1] = g;
        img->buf[i][2] = b;
    }
}

void put_pixel_unsafe(
       	image img,
        unsigned int x,
        unsigned int y,
        color_component r,
        color_component g,
        color_component b )
{
    unsigned int ofs;
    ofs = (y * img->width) + x;
    img->buf[ofs][0] = r;
    img->buf[ofs][1] = g;
    img->buf[ofs][2] = b;
}
 
void put_pixel_clip(
       	image img,
        unsigned int x,
        unsigned int y,
        color_component r,
        color_component g,
        color_component b )
{
    if (x < img->width && y < img->height)
      put_pixel_unsafe(img, x, y, r, g, b);
}

#define PPMREADBUFLEN 256
image get_ppm(FILE *pf)
{
        char buf[PPMREADBUFLEN], *t;
        image img;
        unsigned int w, h, d;
        int r;
 
        if (pf == NULL) return NULL;
        t = fgets(buf, PPMREADBUFLEN, pf);
        if ( (t == NULL) || ( strncmp(buf, "P6\n", 3) != 0 ) ) return NULL;
        do
        { /* Px formats can have # comments after first line */
           t = fgets(buf, PPMREADBUFLEN, pf);
           if ( t == NULL ) return NULL;
        } while ( strncmp(buf, "#", 1) == 0 );
        r = sscanf(buf, "%u %u", &w, &h);
        if ( r < 2 ) return NULL;
        // The program fails if the first byte of the image is equal to 32. because
        // the fscanf eats the space and the image is read with some bit less
        r = fscanf(pf, "%u\n", &d);
        if ( (r < 1) || ( d != 255 ) ) return NULL;
        img = alloc_img(w, h);
        if ( img != NULL )
        {
            size_t rd = fread(img->buf, sizeof(pixel), w*h, pf);
            if ( rd < w*h )
            {
               free_img(img);
               return NULL;
            }
            return img;
        }
}

 
void output_ppm(FILE *fd, image img)
{
  unsigned int n;
  (void) fprintf(fd, "P6\n%d %d\n255\n", img->width, img->height);
  n = img->width * img->height;
  (void) fwrite(img->buf, sizeof(pixel), n, fd);
  (void) fflush(fd);
}

#define MAXCMDBUF 100
void print_jpg(image img, int qual)
{
   char buf[MAXCMDBUF];
   unsigned int n;
   FILE *pipe;
 
   snprintf(buf, MAXCMDBUF, "convert ppm:- -quality %d jpg:-", qual);
   pipe = popen(buf, "w");
   if ( pipe != NULL )
   {
           fprintf(pipe, "P6\n%d %d\n255\n", img->width, img->height);
           n = img->width * img->height;
           fwrite(img->buf, sizeof(pixel), n, pipe);
           pclose(pipe);
   }
}

#define MAXCMDBUF 100
#define MAXFILENAMELEN 256
#define MAXFULLCMDBUF (MAXCMDBUF + MAXFILENAMELEN)
image read_image(const char *name)
{
      FILE *pipe;
      char buf[MAXFULLCMDBUF];
      image im;
 
      FILE *test = fopen(name, "r");
      if ( test == NULL ) {
         fprintf(stderr, "cannot open file %s\n", name);
         return NULL;
      }
      fclose(test);
 
      snprintf(buf, MAXFULLCMDBUF, "convert \"%s\" ppm:-", name);
      pipe = popen(buf, "r");
      if ( pipe != NULL )
      {
           im = get_ppm(pipe);
           pclose(pipe);
           return im;
      }
      return NULL;
}

#define XGET(X) (((X)<0)?0:(((X)>=m.width())?m.width():(X)))
#define YGET(Y) (((Y)<0)?0:(((Y)>=m.height())?m.height():(Y)))
 
struct _pm
{
   pixel p;
   luminance lum;
};

typedef struct {
   image input;
   int x;
   int y;
   int r;
   pixel result;
} task_t;

 
static int _cmp(const void *a, const void *b)
{
   struct _pm *ap, *bp;
   ap = (struct _pm *)a;
   bp = (struct _pm *)b;   
   if ( ap->lum > bp->lum ) return 1;
   if ( ap->lum < bp->lum ) return -1;
   return 0;
}
 
 
static void _get_median(const Image &m,
                  unsigned int x, unsigned int y,
                  int r, pixel *p)
{
     struct _pm *l;
     int i, j;
     unsigned int a,k;
     int flag;
 
     l = (_pm*) malloc((2*r+1)*(2*r+1)*sizeof(struct _pm));
     if ( l != NULL )
     {
     	 flag = 0;
         a = 0;
         for(i=-r; i <= r; i++)
         {
           for(j=-r; j <= r; j++)
           {
           		l[a].p[0] = RED(GET_PIXEL(m,XGET(x+i),YGET(y+j)));
           		l[a].p[1] = GREEN(GET_PIXEL(m,XGET(x+i),YGET(y+j)));
           		l[a].p[2] = BLUE(GET_PIXEL(m,XGET(x+i),YGET(y+j)));           		           		
/*
                for(k=0; k < 3; k++)
                {
                    l[a].p[k] = GET_PIXEL(m,XGET(x+i),YGET(y+j))[k];
                    if(l[a].p[k]==0)
                    	flag = 1;
                }
*/ 
                l[a].lum = (2126*l[a].p[0] + 7152*l[a].p[1] +
                            722*l[a].p[2]) / 10000;
                a++;
           }
         }
     
/*  		 if(flag){*/
/*  		 	int max = 2*r*2*r;*/
/*  		 	fprintf(stderr,"Array non ordinato:\n");*/
/*  		 	for(a=0;a<max;a++){*/
/*  		 		fprintf(stderr,"[%d,%d,%d] ",l[a].p[0],l[a].p[1],l[a].p[2]);*/
/*  		 	}*/
/*  		 	fprintf(stderr,"\n");*/
/*  		 }*/
         qsort(l, (2*r+1)*(2*r+1), sizeof(struct _pm), _cmp);
/*		 if(flag){*/
/*  		 	int max = 2*r*2*r;*/
/*  		 	fprintf(stderr,"Array ordinato:\n");*/
/*  		 	for(a=0;a<max;a++){*/
/*  		 		fprintf(stderr,"[%d,%d,%d] ",l[a].p[0],l[a].p[1],l[a].p[2]);*/
/*  		 	}*/
/*  		 	fprintf(stderr,"\n");*/
/*  		 }         */
         for(k=0;k<3;k++)
         {
           (*p)[k] = l[2*r*r].p[k];
         }
         free(l);
     }
}


extern "C"
{


void applyFilter( const Image &img, Image &dst, const Rect &r )
{
	
	for( int x = r.x; x < r.w; x ++ ) {
		for( int y = r.y; y < r.h; y ++ ) {
		
/*			Rgb p = img.pixel( x, y );			
			
			dst.pixel(x,y) = RGB( GREEN(p), RED(p), BLUE(p) );
*/
			pixel op;
			_get_median(img, x, y, RADIUS, &op);						
			dst.pixel(x,y) = RGB( op[0], op[1], op[2]);
						
		}
	}
}


}
